// dsApiModel
package models

import (
	"bytes"
	"crypto/hmac"

	//"crypto/md5"
	//"crypto/sha1"
	"crypto/sha256"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"

	//"encoding/hex"
	"strings"

	//"lib"

	//"fmt"
	"crypto/rand"
	"fmt"
	"io/ioutil"
	"log"
	"math/big"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"time"
	"unicode/utf8"

	"xwaf_admin/common"
	admlib "xwaf_admin/xadmin/src/lib"

	"github.com/tidwall/gjson"
	"golang.org/x/text/encoding/simplifiedchinese"
)

var TyUrl = admlib.GetConfigString("TyUrl")
var TyAppId = admlib.GetConfigString("TyAppId")
var TyAppSecret = admlib.GetConfigString("TyAppSecret")
var TyAc = admlib.GetConfigString("TyAc")

//请求https忽略证书检查
func RequestByProxy(url_addr, proxy_addr, method string, reqBody string, timeout int, isAuthToken bool) (*http.Response, error) {
	var request *http.Request
	if method == "GET" {
		request, _ = http.NewRequest(method, url_addr, nil)
	} else {
		bodyByte := bytes.NewReader([]byte(reqBody))
		request, _ = http.NewRequest(method, url_addr, bodyByte)
		//request.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
		request.Header.Set("content-type", "application/json;charset=utf-8")
		if isAuthToken {
			request.Header.Set("auth-token", admlib.GetConfigString("AuthToken"))
		}
	}

	if timeout == 0 {
		timeout = admlib.GetConfigInt("RequestTimeOut")
	}

	request.Header.Set("User-Agent", "Gdca-Api")
	//request.Header.Set("Accept-Encoding", "compress, gzip")
	request.Header.Set("Connection", "close")
	request.Close = true

	var pool *x509.CertPool
	if request.URL.Scheme == "https" {
		pool = x509.NewCertPool()
		caCertPath := admlib.GetConfigString("caPath")
		if caCertPath == "" {
			caCertPath = "/etc/pki/tls/certs/ca-bundle.crt"
		}
		caCrt, err1 := ioutil.ReadFile(caCertPath)
		if err1 != nil {
			log.Printf("[error] read cert file fail: %s\n", err1)
		}
		pool.AppendCertsFromPEM(caCrt)
		//pool.AppendCertsFromPEM([]byte(castr))
	}
	var client *http.Client
	var conn net.Conn
	var err error
	//var remoteaddr string
	if proxy_addr != "" {
		proxy_addr = "http://" + proxy_addr
		proxy, err := url.Parse(proxy_addr)
		if err != nil {
			return nil, err
			log.Printf("ERR:GetByProxy err,ip:%s,err:%s\n", proxy_addr, err)
		}
		client = &http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyURL(proxy),
				Dial: func(netw, proxy string) (net.Conn, error) {
					deadline := time.Now().Add(time.Duration(timeout) * time.Second)
					conn, err = net.DialTimeout(netw, proxy, time.Second*time.Duration(timeout))
					if err != nil {
						return nil, err
					}
					conn.SetDeadline(deadline)
					//remoteaddr = conn.RemoteAddr().String()
					return conn, nil
				},
				ResponseHeaderTimeout: time.Second * time.Duration(timeout),
				TLSClientConfig:       &tls.Config{RootCAs: pool},
				//TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			},
		}
	} else {
		client = &http.Client{
			Transport: &http.Transport{
				Dial: func(netw, addr string) (net.Conn, error) {
					deadline := time.Now().Add(time.Duration(timeout) * time.Second)
					conn, err = net.DialTimeout(netw, addr, time.Second*time.Duration(timeout))
					if err != nil {
						return nil, err
					}
					//remoteaddr = conn.RemoteAddr().String()
					conn.SetDeadline(deadline)
					return conn, nil
				},
				ResponseHeaderTimeout: time.Second * time.Duration(timeout),
				TLSClientConfig:       &tls.Config{RootCAs: pool, InsecureSkipVerify: true},
				// TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			},
		}
	}
	// log.Println(request.Header)
	// log.Println(request.Method)
	return client.Do(request)
}

/*
url_addr:request URL
proxy_addr :used proxy or not
reqBody: requestBody
timeout:request Timeout
isAuthToken: set auth-token or not
expire :set expireTime in Redis ,if expire is 0,not used redis
*/
func RequestWithCache(url_addr, proxy_addr, method, reqBody string, timeout int, isAuthToken bool, Keystr string, expire int) (string, error) {

	if expire > 0 && Keystr != "" {
		respBody := common.Get(Keystr)
		if respBody != "" {
			log.Printf("[INFO] Request HIT:%s\n", url_addr)
			return respBody, nil
		}
	}
	var err error
	var resp *http.Response
	startTime := time.Now().UnixNano()
	log.Printf("Request Body:%s\n", reqBody)
	resp, err = RequestByProxy(url_addr, proxy_addr, method, reqBody, timeout, isAuthToken)
	if err != nil {
		log.Printf("Request %s Fail:%s,\n", url_addr, err)
		return "", err
	}
	endTime := time.Now().UnixNano()
	reqtime := float64(endTime-startTime) / 1000000000
	defer resp.Body.Close()
	bodyBytes, _ := ioutil.ReadAll(resp.Body)
	log.Printf("[INFO] Request MISS url:%s,reqTime:%f,statusCode:%d,body:%s\n", url_addr, reqtime, resp.StatusCode, string(bodyBytes))
	if expire > 0 && Keystr != "" {
		gresult := gjson.ParseBytes(bodyBytes)
		if gresult.Get("code").Exists() {
			respcode := gresult.Get("code").Int()
			if respcode == 0 || respcode == 200 { //返回code 为0 或200时才缓存
				log.Printf("[INFO] code Start Set key:%s,code:%d", Keystr, respcode)
				_ = common.SetEx(Keystr, string(bodyBytes), int64(expire))
			}
		} else {
			if gresult.Get("result.total").Int() > 0 { //针对信安工单返回gettargetUser
				log.Printf("[INFO] result Start Set key:%s", Keystr)
				_ = common.SetEx(Keystr, string(bodyBytes), int64(expire))
			} else {
				log.Printf("[WARN] body not include code key\n")
			}
		}
	}
	return string(bodyBytes), nil
}

// get 请求URL
func ReqUrl(turl, methd, xhost, bodystr string) (*http.Response, error) {
	var request *http.Request
	if bodystr != "" && methd == "POST" {
		request, _ = http.NewRequest(methd, turl, bytes.NewReader([]byte(bodystr)))
	} else {
		request, _ = http.NewRequest(methd, turl, nil)
	}
	//request, _ := http.NewRequest(methd, turl, nil)
	request.Header.Set("User-Agent", "PConline-cachetools")
	request.Host = xhost

	client := &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse /* 不进入重定向 */
		},
		Transport: &http.Transport{
			Dial: func(netw, proxy string) (net.Conn, error) {
				deadline := time.Now().Add(10 * time.Second)
				c, err := net.DialTimeout(netw, proxy, time.Second*5)
				if err != nil {
					return nil, err
				}
				c.SetDeadline(deadline)
				return c, nil
			},
		},
	}
	return client.Do(request)
}

//天翼
func hmacSha256Byte(target, key string) []byte {
	h := hmac.New(sha256.New, []byte(key))
	h.Write([]byte(target))
	hashBytes := h.Sum(nil)
	return hashBytes
}

//天翼
func encrypt(content, key string) (signature string, err error) {
	//替换空格为+
	key = strings.ReplaceAll(key, " ", "+")
	//替换-为+号
	key = strings.ReplaceAll(key, "-", "+")
	//替换_为/号
	key = strings.ReplaceAll(key, "_", "/")
	//填充=，字节为4的倍数
	for len(key)%4 != 0 {
		key += "="
	}
	bcode, err := base64.StdEncoding.DecodeString(key)
	if err != nil {
		fmt.Println(err)
		return
	}
	sigbyte := hmacSha256Byte(content, string(bcode))
	sigstr := base64.URLEncoding.EncodeToString(sigbyte)
	signature = strings.Replace(sigstr, "=", "", -1)
	return
}

func getSignature(req *http.Request) (string, string) {
	timestamp_ms := time.Now().Unix() * 1000
	timestamp_day := timestamp_ms / 86400000
	timestamp_ms_str := strconv.FormatInt(timestamp_ms, 10)
	sign_str := fmt.Sprintf("%s\n%v\n%s", TyAppId, timestamp_ms, req.URL.RequestURI())
	identity := fmt.Sprintf("%s:%v", TyAppId, timestamp_day)
	tmp_signature, err := encrypt(identity, TyAppSecret)
	if err != nil {
		fmt.Errorf("encrypt identity failed, err:%v", err)
		return "", ""
	}
	signature, err := encrypt(sign_str, tmp_signature)
	if err != nil {
		fmt.Errorf("encrypt identity failed, err:%v", err)
		return "", ""
	}
	return timestamp_ms_str, signature
}

//判断字符串是否utf8,不是就转成utf8
func IfUtf8AndDecode(srcStr string) string {
	if !utf8.ValidString(srcStr) {
		StrUtf8, err1 := simplifiedchinese.GB18030.NewDecoder().String(srcStr)
		if err1 != nil {
			log.Println(err1)
		}
		return StrUtf8
	}
	return srcStr
}

func CreateRandomString(len int) string {
	var container string
	//var str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
	var str = "abcdefghijkmnpqrstuvwxyz23456789"
	b := bytes.NewBufferString(str)
	length := b.Len()
	bigInt := big.NewInt(int64(length))
	for i := 0; i < len; i++ {
		randomInt, _ := rand.Int(rand.Reader, bigInt)
		container += string(str[randomInt.Int64()])
	}
	return container
}

func CreateCapitalLetters(len int) string {
	var container string
	var str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	b := bytes.NewBufferString(str)
	length := b.Len()
	bigInt := big.NewInt(int64(length))
	for i := 0; i < len; i++ {
		randomInt, _ := rand.Int(rand.Reader, bigInt)
		container += string(str[randomInt.Int64()])
	}
	return container
}

func IsExistInArray(arry []string, substr string) bool {
	var isok bool
	for _, v := range arry {
		if substr == v {
			isok = true
			break
		}
	}
	return isok
}
